package be.xios.ti.droodle;

import java.awt.*;
import java.io.*;
import java.util.*;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

import org.w3c.dom.*;
import org.xml.sax.*;

/**
 * Vervolledig deze klasse zodat tekeningen kunnen geladen
 * en bewaard worden van en naar XML bestanden
 * 
 * @author student
 */
public final class XMLFormat {
	/**
	 * lees het xml bestand f met behulp van SAX en maak
	 * een Drawing object aan. Gebruik hiervoor de klasse
	 * SAXLoad.
	 * 
	 * @param f het xml bestand
	 * @return een Drawing object met de juiste Shape objecten
	 */
	public static Drawing load(File f) {
		System.out.println("Maak Drawing object van "+f.getName());
		try {
		SAXLoader loader = new SAXLoader(f);
		
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser parser = factory.newSAXParser();
		XMLReader reader = parser.getXMLReader();
		reader.setContentHandler(loader);
		reader.setErrorHandler(loader);
		
		InputSource source = new InputSource(new FileInputStream(f));
		reader.parse(source);
		
		return loader.getDrawing();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * maak een DOM tree van de Drawing d en serialiseer naar
	 * het bestand f
	 * @param f het xml bestand
	 * @param d het Drawing object
	 */
	public static void save(File f, Drawing d) {
		System.out.println("Drawing ["+d.getDescription()+"] wordt "+
				"opgeslagen in "+f.getName());
		
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document document = null;
			
			if (f.getName().endsWith(".svg")) {
			    System.out.println("SVG export");
                document = SvgSave(d, db);
                TransformerFactory xformFactory = TransformerFactory.newInstance();
                Transformer idTransform = xformFactory.newTransformer();
                idTransform.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD SVG 1.1//EN");
                idTransform.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd");
                idTransform.setOutputProperty(OutputKeys.INDENT, "yes");
                
                Source input = new DOMSource(document);
                Result output = new StreamResult(f);
                idTransform.transform(input, output);
            } else {
                System.out.println("Gewone export");
                document = generalSave(d, db);
                TransformerFactory xformFactory = TransformerFactory.newInstance();
                Transformer idTransform = xformFactory.newTransformer();
                Source input = new DOMSource(document);
                Result output = new StreamResult(f);
                idTransform.transform(input, output);
            }
			

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
     * @param d
     * @param document
     */
    private static Document SvgSave(Drawing d, DocumentBuilder db) {
        String colorString = null;
        
        Document document = db.getDOMImplementation().createDocument("http://www.w3.org/2000/svg","svg",null);
        Element root = document.getDocumentElement();
        
        Element tmp = null;
        Text tmpText = null;
        
        //create the description element <desc>
        tmp = document.createElement("desc");
        tmpText = document.createTextNode(d.getDescription());
        tmp.appendChild(tmpText);
        root.appendChild(tmp);
        
        //vervolledig de DOM tree
        ArrayList shapes = d.getShapes();
        Iterator iter = shapes.iterator();
        Shape s = null;
        while (iter.hasNext()) {
            s = (Shape)iter.next();
            
            Element shapeElem = null;
            if (s instanceof CircleShape) {
                shapeElem = document.createElement("circle");
            } else if (s instanceof LineShape) {
                shapeElem = document.createElement("line");
            } else if (s instanceof RectangleShape) {
                shapeElem = document.createElement("rect");
            } else {
                throw new RuntimeException("Unknown Shape: "+s.getClass());
            }
            
            colorString = "rgb("+
                           s.getColor().getRed()+","+
                           s.getColor().getGreen()+","+
                           s.getColor().getBlue()+")";
            shapeElem.setAttribute("stroke", colorString);
            shapeElem.setAttribute("stroke-width", Integer.toString(s.getSize()));
            
            if (s.isFilled()) {
                shapeElem.setAttribute("fill",colorString);
            } else {
                shapeElem.setAttribute("fill", "none");
            }
            if (s instanceof CircleShape) {
                CircleShape cShape = (CircleShape)s;
                shapeElem.setAttribute("r", Integer.toString(cShape.getRadius()));
                shapeElem.setAttribute("cx", Integer.toString(s.getX()));
                shapeElem.setAttribute("cy", Integer.toString(s.getY()));
                
            } else if (s instanceof LineShape) {
                LineShape lShape = (LineShape)s;
                shapeElem.setAttribute("x2", Integer.toString(lShape.getX2()));
                shapeElem.setAttribute("y2", Integer.toString(lShape.getY2()));
                shapeElem.setAttribute("x1", Integer.toString(s.getX()));
                shapeElem.setAttribute("y1", Integer.toString(s.getY()));
                
            } else if (s instanceof RectangleShape) {
                RectangleShape rShape = (RectangleShape)s;
                int w = Math.abs(rShape.getX()-rShape.getX2());
                int h = Math.abs(rShape.getY()-rShape.getY2());
                shapeElem.setAttribute("width", Integer.toString(w));
                shapeElem.setAttribute("height", Integer.toString(h));
                shapeElem.setAttribute("x", Integer.toString(s.getX()));
                shapeElem.setAttribute("y", Integer.toString(s.getY()));
                
            } else {
                throw new RuntimeException("Unknown Shape: "+s.getClass());
            }
            
            root.appendChild(shapeElem);
        }
        
        return document;
    }
    
    /**
     * @param d
     * @param document
     */
    private static Document generalSave(Drawing d, DocumentBuilder db) {
        Document document = db.newDocument();
        Element root = document.createElement("drawing");
        root.setAttribute("description", d.getDescription());
        root.setAttribute("background",getColorString(d.getBackground()));			
        document.appendChild(root);
        
        //vervolledig de DOM tree
        ArrayList shapes = d.getShapes();
        Iterator iter = shapes.iterator();
        Shape s = null;
        while (iter.hasNext()) {
        	s = (Shape)iter.next();
        	Element shapeElem = null;
        	if (s instanceof CircleShape) {
        		shapeElem = document.createElement("circle");
        	} else if (s instanceof LineShape) {
        		shapeElem = document.createElement("line");
        	} else if (s instanceof RectangleShape) {
        		shapeElem = document.createElement("rectangle");
        	} else {
        		throw new RuntimeException("Unknown Shape: "+s.getClass());
        	}
        	
        	shapeElem.setAttribute("color", getColorString(s.getColor()));
        	shapeElem.setAttribute("size", Integer.toString(s.getSize()));
        	shapeElem.setAttribute("x", Integer.toString(s.getX()));
        	shapeElem.setAttribute("y", Integer.toString(s.getY()));
        	shapeElem.setAttribute("filled", Boolean.toString(s.isFilled()));
        	
        	if (s instanceof CircleShape) {
        		CircleShape cShape = (CircleShape)s;
        		shapeElem.setAttribute("radius", Integer.toString(cShape.getRadius()));
        	} else if (s instanceof LineShape) {
        		LineShape lShape = (LineShape)s;
        		shapeElem.setAttribute("x2", Integer.toString(lShape.getX2()));
        		shapeElem.setAttribute("y2", Integer.toString(lShape.getY2()));
        	} else if (s instanceof RectangleShape) {
        		RectangleShape rShape = (RectangleShape)s;
        		shapeElem.setAttribute("x2", Integer.toString(rShape.getX2()));
        		shapeElem.setAttribute("y2", Integer.toString(rShape.getY2()));
        	} else {
        		throw new RuntimeException("Unknown Shape: "+s.getClass());
        	}
        	
        	root.appendChild(shapeElem);
        }
        
        return document;
    }

    /**
	 * maak een kleur aan op basis van een string
	 * @param color formaat "#rrggbb", bv "#00ff14"
	 * @return een nieuwe kleur
	 */
	public static Color createColor(String color) {
		Color c = null;
		int r=0, g=0, b=0;
		
		r = Integer.parseInt(color.substring(1,3), 16);
		g = Integer.parseInt(color.substring(3,5),16);
		b = Integer.parseInt(color.substring(5,7),16);
		
		c = new Color(r,g,b);
		return c;
	}
	
	public static String getColorString(Color c) {
		StringBuffer b = new StringBuffer();
		b.append("#");
		if (c.getRed() < 16)
			b.append("0");
		b.append(Integer.toHexString(c.getRed()));
		
		if (c.getGreen() < 16)
			b.append("0");
		b.append(Integer.toHexString(c.getGreen()));
		
		if (c.getBlue() < 16)
			b.append("0");
		b.append(Integer.toHexString(c.getBlue()));
		
		return b.toString();
	}
	
	/**
	 * testprogramma, kopieert een tekening eerst naar een Drawing
	 * en vervolgens opnieuw naar een bestand.
	 * @param args args[0] is src, args[1] is target
	 */
	public static void main(String[] args) {
		if (args.length != 2) {
			System.err.println("usage: XMLFormat <src> <dest>");
			System.exit(1);
		}
		Drawing d = load(new File(args[0]));
		save(new File(args[1]),d);
	}
}
